package org.iabako.server.controller.timer.demo;

import org.apache.log4j.Logger;
import org.iabako.server.tools.ServerTools;
import org.iabako.shared.entity.business.*;
import org.iabako.shared.entity.enumeration.InstallmentsType;
import org.iabako.shared.entity.enumeration.PaymentMethod;
import org.iabako.shared.entity.enumeration.PaymentType;
import org.iabako.shared.entity.enumeration.ProductType;
import org.iabako.shared.entity.user.Enterprise;
import org.iabako.shared.exception.*;
import org.iabako.shared.iservice.business.ClientService;
import org.iabako.shared.iservice.business.ProductService;
import org.iabako.shared.iservice.business.SaleService;
import org.iabako.shared.iservice.business.ServiceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;

/**
 * Created by jose on 07/01/15.
 */

@Controller
public class CreateSaleController {

    static Logger log = Logger.getLogger(CreateSaleController.class.getName());

    @Autowired
    ProductService productService;
    @Autowired
    ServiceService serviceService;
    @Autowired
    SaleService saleService;
    @Autowired
    ClientService clientService;

    //This is thread unsafe but it doesn't matter because it is used once by system and not by users
    List<String> clientEmails = new ArrayList<String>();
    List<Product> products = new ArrayList<Product>();
    List<Service> services = new ArrayList<Service>();
    Enterprise enterprise;

    public void initialize() {
        clientEmails = new ArrayList<String>();
        products = new ArrayList<Product>();
        services = new ArrayList<Service>();
        enterprise = null;
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void create(Enterprise enterprise, String csvFileProducts, String csvFileServices, String csvFileClients) throws IOException, URISyntaxException {

        this.enterprise = enterprise;

        products = getProductsFromCSV(enterprise, csvFileProducts);
        services = getServicesFromCSV(enterprise, csvFileServices);

        for (Product product : products) {
            try {
                productService.save(product);
            } catch (IabakoException e) {
                log.error(e.getMessage(), e);
                //should never happen
            }
        }

        for (Service service : services) {
            try {
                serviceService.save(service);
            } catch (IabakoException e) {
                log.error(e.getMessage(), e);
                //should never happen
            }
        }

        clientEmails = getClientsFromCSV(csvFileClients);

        ServerTools serverTools = new ServerTools();
        Date now = new Date();

        Date nMines2 = serverTools.addYearsToDate(now, -2);
        Calendar calendarMines2 = Calendar.getInstance();
        calendarMines2.setTime(nMines2);
        calendarMines2.set(Calendar.DAY_OF_MONTH, 1);

        Date nMines1 = serverTools.addYearsToDate(now, -1);

        Date saleDate = null;
        Integer numberOfSalesPerMonth = 0;
        Integer indexRandom = 0;

        //Random one-shot Sales in two last years and current
        while (calendarMines2.getTime().compareTo(now) < 0) {

            if (calendarMines2.getTime().compareTo(nMines1) < 0) {
                //N-2
                numberOfSalesPerMonth = new Double(Math.random() * 5).intValue();
            } else {
                //N-1
                numberOfSalesPerMonth = 4;
            }

            for (int i = 0; i < numberOfSalesPerMonth; i++) {
                Service service = null;
                Product product = null;
                indexRandom = new Double(Math.random() * 9).intValue();

                if (i % 2 == 0) {
                    product = products.get(indexRandom);
                } else {
                    service = services.get(indexRandom);
                }

                saleDate = calendarMines2.getTime();
                makeSale(product, service, saleDate, 0, 0, 0);
            }

            calendarMines2.add(Calendar.MONTH, 1);
        }
    }

    public List<Product> getProductsFromCSV(Enterprise enterprise, String csvFileProducts) throws IOException, URISyntaxException {
        URL csvFileURL = this.getClass().getClassLoader().getResource(csvFileProducts);
        BufferedReader br = new BufferedReader(new FileReader(new File(csvFileURL.toURI())));
        String line = "";

        int index = 0;

        List<Product> products = new ArrayList<Product>();

        while ((line = br.readLine()) != null) {
            if (index++ == 0) {
                //skip header
                continue;
            }

            String[] token = line.split(";");
            if (token.length == 6) {
                Product product = new Product();
                product.setNumber(token[0]);
                product.setName(token[1]);
                product.setDescription(token[2]);
                if (Double.parseDouble(token[3]) > 0) {
                    product.setProductType(ProductType.resale);
                    product.setPurchasePrice(Double.parseDouble(token[3]));
                } else {
                    product.setProductType(ProductType.manufactured);
                }
                product.setPrice(Double.parseDouble(token[4]));
                product.setQuantity(Integer.parseInt(token[5]));

                product.setEnterprise(enterprise);

                products.add(product);
            }
        }
        return products;
    }

    public List<Service> getServicesFromCSV(Enterprise enterprise, String csvFileServices) throws IOException, URISyntaxException {
        URL csvFileURL = this.getClass().getClassLoader().getResource(csvFileServices);
        BufferedReader br = new BufferedReader(new FileReader(new File(csvFileURL.toURI())));
        String line = "";

        int index = 0;

        List<Service> services = new ArrayList<Service>();

        while ((line = br.readLine()) != null) {
            if (index++ == 0) {
                //skip header
                continue;
            }

            String[] token = line.split(";");
            if (token.length == 4) {
                Service service = new Service();
                service.setNumber(token[0]);
                service.setName(token[1]);
                service.setDescription(token[2]);
                service.setInstallmentType(InstallmentsType.defined);
                service.setPrice(Double.parseDouble(token[3]));

                service.setEnterprise(enterprise);

                services.add(service);
            }
        }
        return services;
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void createDailySales(Enterprise enterprise, String csvFileProducts, String csvFileServices, String csvFileClients) throws IOException, URISyntaxException {
        int randomIndex;
        Date now = new Date();

        if (this.enterprise == null) {
            this.enterprise = enterprise;
        }

        if (products.isEmpty()) {
            products = getProductsFromCSV(enterprise, csvFileProducts);
        }

        if (services.isEmpty()) {
            services = getServicesFromCSV(enterprise, csvFileServices);
        }

        if (clientEmails.isEmpty()) {
            clientEmails = getClientsFromCSV(csvFileClients);
        }

        randomIndex = new Double(Math.random() * 9).intValue();
        Product product = products.get(randomIndex);
        makeSale(product, null, now, 0, 0, 0);

        randomIndex = new Double(Math.random() * 9).intValue();
        Service service = services.get(randomIndex);
        makeSale(null, service, now, 0, 0, 0);

        Service serviceWithInstallments = services.get(services.size() - 1);
        makeSale(null, serviceWithInstallments, now, 1, 5, 4);
    }

    private void makeSale(Product product, Service service, Date saleDate, int payedInstallments, int pendingInstallments, int futureInstallments) {

        ServerTools serverTools = new ServerTools();
        Integer indexRandom = new Double(Math.random() * 100).intValue();
        String email = clientEmails.get(indexRandom);

        Client client = clientService.getClientByEmail(enterprise, email);
        Product productFromDB = null;
        Service serviceFromDB = null;
        Double price = null;

        if (product != null) {
            productFromDB = productService.getByNumber(enterprise, product.getNumber());
            price = productFromDB.getPrice();
        } else {
            serviceFromDB = serviceService.getByNumber(enterprise, service.getNumber());
            price = serviceFromDB.getPrice();
        }

        Sale sale = new Sale();
        sale.setNumber(new Date().getTime() + "");
        sale.setClient(client);
        sale.setDate(saleDate);
        sale.setEnterprise(enterprise);
        sale.setPaymentMethod(PaymentMethod.cash);
        sale.setPaymentType(PaymentType.onePayment);

        SaleProductService saleProductService = new SaleProductService();
        saleProductService.setProduct(productFromDB);
        saleProductService.setService(serviceFromDB);
        saleProductService.setPrice(price);
        saleProductService.setQuantity(1);
        saleProductService.setSale(sale);

        List<Payment> payments = new ArrayList<Payment>();
        List<Installment> installments = new ArrayList<Installment>();

        int numberOfInstallments = payedInstallments + pendingInstallments + futureInstallments;
        int pastInstallments = payedInstallments + pendingInstallments;

        Double installmentPrice = numberOfInstallments > 0 ? price / numberOfInstallments : null;
        Double priceSum = 0D;

        if (numberOfInstallments > 0) {
            //sale with installments
            Date installmentDate = serverTools.addMonthsToDate(new Date(), (pastInstallments - 1) * -1);
            for (int i = 0; i < payedInstallments; i++) {
                Payment payment = Payment.getPayment(null, installmentDate, installmentPrice, sale);
                priceSum += installmentPrice;
                payments.add(payment);
                installmentDate = serverTools.addMonthsToDate(installmentDate, 1);
            }
            for (int i = 0; i < pendingInstallments; i++) {
                Installment installment = Installment.getInstallment(installmentDate, installmentPrice, sale);
                priceSum += installmentPrice;
                installments.add(installment);
                installmentDate = serverTools.addMonthsToDate(installmentDate, 1);
            }
            for (int i = 0; i < futureInstallments; i++) {
                Installment installment = Installment.getInstallment(installmentDate, installmentPrice, sale);
                priceSum += installmentPrice;
                installments.add(installment);
                installmentDate = serverTools.addMonthsToDate(installmentDate, 1);
            }

            Installment lastInstallment = installments.get(installments.size() - 1);
            lastInstallment.setAmount(price - (priceSum - lastInstallment.getAmount()));

        } else {
            //one shot sale
            Payment payment = Payment.getPayment(null, saleDate, price, sale);
            payments.add(payment);
        }

        try {
            saleService.save(false, sale, Arrays.asList(saleProductService), payments, installments);
        } catch (IabakoPackageForbiddenException e) {
            log.error(e.getMessage(), e);
            //should never happen
        } catch (IabakoActionForbiddenException e) {
            log.error(e.getMessage(), e);
            //should never happen
        } catch (IabakoUniqueConstraintException e) {
            log.error(e.getMessage(), e);
            //should never happen
        } catch (IabakoStockException e) {
            log.error(e.getMessage(), e);
            //should never happen
        }
    }

    private List<String> getClientsFromCSV(String csvFileClients) throws IOException, URISyntaxException {

        List<String> clientEmails = new ArrayList<String>();

        URL csvFileURL = this.getClass().getClassLoader().getResource(csvFileClients);
        BufferedReader br = new BufferedReader(new FileReader(new File(csvFileURL.toURI())));
        String line = "";
        int index = 0;

        while ((line = br.readLine()) != null) {
            if (index++ == 0) {
                //skip header
                continue;
            }

            String[] token = line.split(";");
            if (token.length == 10) {
                clientEmails.add(token[3]);
            }
        }

        return clientEmails;
    }
}
